package com.jmcgg.freesky.common.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.eclipse.persistence.config.CascadePolicy;
import org.eclipse.persistence.config.HintValues;
import org.eclipse.persistence.config.QueryHints;

public class BaseJpaDAO<T> {

	private EntityManager entityManager;

	public EntityManager getEntityManager() {
		return this.entityManager;
	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	private Class<T> clazz;

	/**
	 * 用于Dao层子类使用的构造函数. 通过子类的泛型定义取得对象类型Class. eg. public class UserDao extends
	 * JpaTopLinkDao<User>
	 */
	@SuppressWarnings("unchecked")
	public BaseJpaDAO() {
		this.clazz = (Class<T>) ((ParameterizedType) getClass()
				.getGenericSuperclass()).getActualTypeArguments()[0];
	}

	/**
	 * 用于省略Dao层, 在Service层直接使用通用JpaTopLinkDao的构造函数. 在构造函数中定义对象类型Class. eg.
	 * JpaTopLinkDao<User> userDao = new JpaTopLinkDao<User>(User.class);
	 */
	public BaseJpaDAO(final Class<T> clazz) {
		this.clazz = clazz;
	}

	public void setClazz(Class<T> clazz) {
		this.clazz = clazz;
	}

	public Class<T> getClazz() {
		return this.clazz;
	}

	/**
	 * 保存新增的对象.
	 */
	public void save(final T entity) {
		this.getEntityManager().persist(entity);
	}

	/**
	 * 
	 * 更新对象
	 */
	public void update(T entity) {
		this.getEntityManager().merge(entity);
	}

	/**
	 * 删除对象.
	 * 
	 * @param entity
	 *            对象必须是session中的对象或含id属性的transient对象.
	 */
	public void delete(final T entity) {
		if (entity != null) {
			Object _object = getEntityManager().merge(entity);
			if (_object == null) {
				return;
			}
			this.getEntityManager().remove(_object);
			this.getEntityManager().flush();
		}
	}

	/**
	 * Flush.
	 */
	public void flush() {
		EntityManager em = this.getEntityManager();
		em.getTransaction().commit();
	}

	/**
	 * 按id删除对象.
	 */
	public void delete(final Serializable id) {
		if (id != null) {
			T entity = this.findById(id);
			delete(entity);
		}
	}

	/**
	 * 获取所有对象
	 */
	public List<T> findAll() {
		return findBy(null, null);
	}

	/**
	 * 按id获取对象.
	 */
	public T findById(final Serializable id) {
		if (id == null)
			return null;
		return this.getEntityManager().find(clazz, id);
	}

	/**
	 * 按属性查找对象列表,匹配方式为相等.
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBy(final String propertyName, final Object value) {
		EntityManager em = this.getEntityManager();
		String clazzName = clazz.getSimpleName();
		StringBuffer sb = new StringBuffer("select obj from ");
		sb.append(clazzName).append(" obj");
		Query query = null;
		if (propertyName != null && value != null) {
			sb.append(" where obj.").append(propertyName).append("=:value");
			query = em.createQuery(sb.toString()).setParameter("value", value);
		} else {
			query = em.createQuery(sb.toString());
		}
		return (List<T>) query.getResultList();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByFromDB(final String propertyName, final Object value) {
		EntityManager em = this.getEntityManager();
		String clazzName = clazz.getSimpleName();
		StringBuffer sb = new StringBuffer("select obj from ");
		sb.append(clazzName).append(" obj");
		Query query = null;
		if (propertyName != null && value != null) {
			sb.append(" where obj.").append(propertyName).append("=:value");
			query = em.createQuery(sb.toString()).setParameter("value", value);
		} else {
			query = em.createQuery(sb.toString());
		}
		query.setHint(QueryHints.REFRESH, HintValues.TRUE);
		query.setHint(QueryHints.REFRESH_CASCADE, CascadePolicy.CascadeAllParts);
		return query.getResultList();
	}

	/**
	 * 按map属性查找对象列表,匹配方式为相等.
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBy(final Map<String, Object> m) {
		EntityManager em = this.getEntityManager();
		String clazzName = clazz.getSimpleName();
		StringBuffer sb = new StringBuffer("select obj from ");
		sb.append(clazzName).append(" obj where ");

		Boolean bFirst = true;
		Set<Entry<String, Object>> vSet = m.entrySet();
		for (Entry<String, Object> e : vSet) {
			String propertyName = e.getKey();
			Object value = e.getValue();
			if (bFirst) {
				bFirst = false;
			} else {
				sb.append(" and ");
			}
			if (propertyName != null && value != null) {
				sb.append("obj.").append(propertyName).append("=:")
						.append(propertyName);
			}
		}
		Query query = em.createQuery(sb.toString());

		for (Entry<String, Object> e : vSet) {
			String propertyName = e.getKey();
			Object value = e.getValue();

			query = query.setParameter(propertyName, value);

		}

		return (List<T>) query.getResultList();
	}

	/**
	 * 按属性查找唯一对象,匹配方式为相等.
	 */
	public T findUniqueBy(final String propertyName, final Object value) {
		List<T> ret = findBy(propertyName, value);
		if (ret != null && ret.size() > 0) {
			return ret.get(0);
		} else {
			return null;
		}
	}

	/**
	 * 按SQL查询对象列表.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByJpql(final String jpql, final Object... values) {
		EntityManager em = this.getEntityManager();
		Query query = em.createNativeQuery(jpql, clazz);
		int parameterIndex = 1;
		if (values != null && values.length > 0) {
			for (Object obj : values) {
				query.setParameter(parameterIndex++, obj);
			}
		}
		return query.getResultList();
	}

	/**
	 * 按jpql查询唯一对象.
	 * 
	 * @param values
	 *            数量可变的参数,按顺序绑定.
	 */
	public T findUniqueByJpql(final String jpql, final Object... values) {
		List<T> ret = findByJpql(jpql, values);
		if (ret != null && ret.size() > 0) {
			return ret.get(0);
		} else {
			return null;
		}
	}

	/**
	 * 执行SQL进行批量修改/删除操作.
	 */
	public int batchExecute(final String jpql, final Object... values) {
		EntityManager em = this.getEntityManager();
		Query query = em.createQuery(jpql);
		int parameterIndex = 1;
		if (values != null && values.length > 0) {
			for (Object obj : values) {
				query.setParameter(parameterIndex++, obj);
			}
		}
		return query.executeUpdate();
	}
}